#!/usr/bin/env ruby

#
# Ruby class definition of the myCA autoplayer
#

require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'mechanize'
require 'net/http'
require 'irb'
require 'parseconfig'
require 'logger'
require 'thread'

module CAAutoplayer
	@WEBAGENT
	@ACCOUNT
	@USERINFOS
	@RESSOURCES
	@ARMY
	@BANK
	@BASEURL = "http://apps.facebook.com/castle_age/"
	@CONFIGURATION
	@LOGGER

	def CAAutoplayer.baseurl 
		@BASEURL
	end 

	def CAAutoplayer.webagent 
		@WEBAGENT
	end 

	def CAAutoplayer.account 
		@ACCOUNT
	end 

	def CAAutoplayer.user
		@USERINFOS
	end
	
	def CAAutoplayer.ressources
		@RESSOURCES
	end
	
	def CAAutoplayer.army
		@ARMY
	end

	def CAAutoplayer.bank
		@BANK
	end

	def CAAutoplayer.config
		@CONFIGURATION
	end

	def CAAutoplayer.logger
		@LOGGER
	end

	def CAAutoplayer.initialize #{{{
		@LOGGER = Logger.new('caap.log', 0, 100 * 1024 * 1024)
		@CONFIGURATION = ParseConfig.new('./caap.cnf')
	
		case @CONFIGURATION.params['global']['loggerLevel'] 
		when "debug"
			@LOGGER.level = Logger::DEBUG
		when "info"
			@LOGGER.level = Logger::INFO
		when "warn"
			@LOGGER.level = Logger::WARN
		when "error"
			@LOGGER.level = Logger::ERROR
		when "fatal"
			@LOGGER.level = Logger::FATAL
		else
			@LOGGER.level = Logger::WARN
		end
		
		@WEBAGENT = Mechanize.new()
		@ACCOUNT = Account.new()
		@USERINFOS = UserInfos.new()
		@RESSOURCES = Ressources.new()
		@ARMY = Army.new()
		@BANK = Bank.new()
		launchworkers()
		
		Shell.new()
	end #}}}

	def CAAutoplayer.launchworkers #{{{
		@LOGGER.info('launchworkers') {"Start the workers..."}
		mutex = Mutex.new

		Thread.new do
			loop do
				mutex.synchronize do
					@USERINFOS.update
				end
				sleep(@CONFIGURATION.params['userinfos']['review'].to_i)
			end
		end

		Thread.new do
			loop do
				mutex.synchronize do
					@RESSOURCES.update
				end
				sleep(@CONFIGURATION.params['ressources']['review'].to_i)
			end
		end

		Thread.new do
			loop do
				mutex.synchronize do
					@BANK.update
				end
				sleep(@CONFIGURATION.params['bank']['review'].to_i)
			end
		end
		
		Thread.new do
			loop do
				mutex.synchronize do
					@ARMY.update
				end
				sleep(@CONFIGURATION.params['army']['review'].to_i)
			end
		end
	end #}}}

	class Bank #{{{
		attr_accessor	:purses
		
		def update
			CAAutoplayer.logger.info(self.class.name.to_s) {"Update bank from castleage"}
			begin
				@page = CAAutoplayer.webagent.get(CAAutoplayer.baseurl)
				if @page.code.to_i != 200
					CAAutoplayer.logger.warn(self.class.name.to_s) {"Facebook/Castleage seems done. (code : #{@page.code})"}
					return
				end
				@doc = Hpricot(@page.body)
				@subdiv = @doc.search('div[@class="st_row_2"]').search('div')
				@purses = @subdiv[0].inner_text.strip.tr('$,', '')
			rescue Exception => e
				CAAutoplayer.logger.error(self.class.name.to_s) { "Unable to update : #{e} #{e.backtrace.join('\n')}"}
			else
				CAAutoplayer.logger.info(self.class.name.to_s) {"Updating ressources is successful"}
			ensure
				CAAutoplayer.logger.info(self.class.name.to_s) {"End of updating ressources from castleage"}
			end
		end
	end #}}}

	class Ressources #{{{
		attr_accessor	:energy_current
		attr_accessor	:energy_max
		attr_accessor	:stamina_current
		attr_accessor	:stamina_max
		
		def update
			CAAutoplayer.logger.info(self.class.name.to_s) {"Update ressources from castleage"}
			begin
				@page = CAAutoplayer.webagent.get(CAAutoplayer.baseurl)
				if @page.code.to_i != 200
					CAAutoplayer.logger.warn(self.class.name.to_s) {"Facebook/Castleage seems done. (code : #{@page.code})"}
					return
				end
				@doc = Hpricot(@page.body)
				@subdiv = @doc.search('div[@class="st_row_2"]').search('div')
				@energy_current = @subdiv[1].inner_text.strip.split('/')[0]
				@energy_max = @subdiv[1].inner_text.strip.split('/')[1]
				@stamina_current = @subdiv[3].inner_text.strip.split('/')[0]
				@stamina_max = @subdiv[3].inner_text.strip.split('/')[1]
			rescue Exception => e
				CAAutoplayer.logger.error(self.class.name.to_s) {"Unable to update ressources : #{e} #{e.backtrace.join('\n')}"}
			else
				CAAutoplayer.logger.info(self.class.name.to_s) {"Updating ressources is successful"}
			ensure
				CAAutoplayer.logger.info(self.class.name.to_s) {"End of updating ressources from castleage"}
			end
		end
	end #}}}

	class UserInfos #{{{
		attr_accessor	:ca_userid
		attr_accessor	:health_current
		attr_accessor	:health_max
		attr_accessor	:experience_current
		attr_accessor	:experience_tolvl
		attr_accessor	:level

		def update
			CAAutoplayer.logger.info(self.class.name.to_s) {"Update user informations from castleage"}
			begin
				@page = CAAutoplayer.webagent.get(CAAutoplayer.baseurl)
				if @page.code.to_i != 200
					CAAutoplayer.logger.warn(self.class.name.to_s) {"Facebook/Castleage seems done. (code : #{@page.code})"}
					return
				end
				@ca_userid = @page.body.to_s.match(/Env=\{user:(\d+),.*?\}/)[1]
				@doc = Hpricot(@page.body)
				@subdiv = @doc.search('div[@class="st_row_2"]').search('div')
				@health_current = @subdiv[2].inner_text.strip.split('/')[0]
				@health_max = @subdiv[2].inner_text.strip.split('/')[1]
				@experience_current = @subdiv[5].inner_text.strip.split('/')[0]
				@experience_tolvl = @subdiv[5].inner_text.strip.split('/')[1]
				@level = @page.body.to_s.match(/Level: (\d+)!/)[1]
			rescue Exception => e
				CAAutoplayer.logger.error(self.class.name.to_s) {"Unable to update #{e} #{e.backtrace.join('\n')}"}
			else
				CAAutoplayer.logger.info(self.class.name.to_s) {"Updating user informations is successful"}
			ensure
				CAAutoplayer.logger.info(self.class.name.to_s) {"End of updating user informations from castleage"}
			end
		end
	end #}}}

	class Player #{{{
		attr_accessor	:name
		attr_accessor	:casuser
		attr_accessor	:level
		attr_accessor	:battlerank
		attr_accessor	:battle_won
		attr_accessor	:battle_lost
		attr_accessor	:quests
		attr_accessor	:eliminate
		attr_accessor	:eliminated
	end #}}}

	class Army #{{{
		attr_accessor	:army_url
		attr_accessor	:list

		def initialize
			@list = Array.new
			@army_url = "army_member.php"
		end

		def update
			CAAutoplayer.logger.info(self.class.name.to_s) {"Update army informations from castleage"}
			@list = Array.new
			begin
				@page = CAAutoplayer.webagent.get(CAAutoplayer.baseurl + @army_url)
				if @page.code.to_i != 200
					CAAutoplayer.logger.warn(self.class.name.to_s) {"Facebook/Castleage seems done. (code : #{@page.code})"}
					return
				end
				@matches = @page.body.to_s.scan(/army_member\.php\?page=(\d+)/)
				@nbpages = @matches.last.last.to_i
				@iterator = 1

				while  @iterator <= @nbpages
					@page_army = CAAutoplayer.webagent.get(CAAutoplayer.baseurl + @army_url + "?page=#{@iterator}")
					@matches = @page_army.body.to_s.scan(/http:\/\/apps\.facebook\.com\/castle_age\/keep\.php\?casuser=(\d+)/)
					@matches.uniq.sort.each { |id|
						CAAutoplayer.logger.debug(self.class.name.to_s) {"visiting #{CAAutoplayer.baseurl}keep.php?casuser=#{id.to_s}"}
						@player = Player.new
						@user_page = CAAutoplayer.webagent.get(CAAutoplayer.baseurl + "keep.php?casuser=#{id.to_s}")
						@doc = Hpricot(@user_page.body)
						@stats = @doc.search('span[@class="linkwhite"]')
						@player.name = @stats.inner_text.match(/"(.*?)", [lL]evel/)[1]
						@player.casuser = id.to_s
						@player.level = @stats.inner_text.match(/".*?", [lL]evel (\d+)/)[1]
						@player.battlerank = @stats.inner_text.match(/".*?", [lL]evel \d+ (.*)/)[1]
						
						@stats2 = @doc.search('div[@class="statsT2"]').search('div:eq(2)')
						@stats2.search('div').each do |divs|
							@info = divs.search('span:eq(1)').inner_text 
							case divs.search('span:eq(0)').inner_text
							when "Quests Completed"
								@player.quests = @info.to_i
							when "Battles/Wars Won"
								@player.battle_won = @info.to_i
							when "Battles/Wars Lost"
								@player.battle_lost = @info.to_i
							when "Times eliminated"
								@player.eliminated = @info.to_i
							when "Times you eliminated an enemy"
								@player.eliminate = @info.to_i
							end
						end
						@list << @player
					}
					@iterator += 1
				end
			rescue Exception => e
				CAAutoplayer.logger.error(self.class.name.to_s) {"Unable to update #{e} #{e.backtrace.join('\n')}"}
			else
				CAAutoplayer.logger.info(self.class.name.to_s) {"Updating army informations is successful"}
			ensure
				CAAutoplayer.logger.info(self.class.name.to_s) {"End of updating army informations from castleage"}
			end
		end

		def display(tolist = @list)
			tolist.each do |player|
				printf("Name: %-15s | Level: %-4s | Quests: %-6s | BWon: %-6s | BLost: %-6s | Elimated: %-6s | Eliminate: %-6s\n", player.name, player.level, player.quests, player.battle_won, player.battle_lost, player.eliminated, player.eliminate)
			end

			return true
		end

		def sortby(attr)
			case attr
			when "level"
				@sorted_player = @list.sort_by do |player|
					player.level
				end
			when "name"
				@sorted_player = @list.sort_by do |player|
					player.name
				end
			end
			
			return @sorted_player
		end

		def findby(attr, cond)
			@find_player = Array.new

			case attr
			when "max-level"
				@list.each do |player|
					if player.level.to_i <= cond.to_i
						@find_player << player
					end
				end
			when "min-level"
				@list.each do |player|
					if player.level.to_i >= cond.to_i
						@find_player << player
					end
				end
			when "level"
				@list.each do |player|
					if player.level.to_i == cond.to_i
						@find_player << player
					end
				end
			when "name"
				@list.each do |player|
					if player.name.match(/#{cond.to_s}/)
						@find_player << player
					end
				end
			else
				puts "list of available attributes are :"
				puts "max-level :\tfind all player with the level specified and below"
				puts "min-level :\tfind all player with the level specified and above"
				puts "level :\tfind all player with the exact level specified"
				puts "name :\tfind all player with the text specified in the name"
			end
			
			return @find_player
		end
	end #}}}

	class Account #{{{
		@@url_login = "http://m.facebook.fr/"

		def initialize
			identify()
		end

		def identify()
			CAAutoplayer.logger.info(self.class.name.to_s) {"Authenticate user on facebook website"}
			begin
				@page = CAAutoplayer.webagent.get(@@url_login)
				login_form = @page.forms.first
				login_form['email'] = CAAutoplayer.config.params['Account']['login']
				login_form['pass'] = CAAutoplayer.config.params['Account']['password']
				@page = CAAutoplayer.webagent.submit(login_form)
			rescue Exception => e
				CAAutoplayer.logger.error(self.class.name.to_s) {"Unable to authenticate #{e} #{e.backtrace.join('\n')}" }
			else
				CAAutoplayer.logger.info(self.class.name.to_s) {"Authentication on facebook is successful"}
			ensure
				CAAutoplayer.logger.info(self.class.name.to_s) {"End of authentication on facebook website"}
			end
		end
	end #}}}
	
	class Shell < IRB::Irb
		def initialize()
			IRB.start()
		end
	end

end
